package com.abel.bigwater.data.kente

import com.abel.bigwater.data.BcdString
import com.abel.bigwater.data.NotEnoughDataException
import com.abel.bigwater.data.UnknownPacketException
import com.alibaba.fastjson.JSON
import io.netty.buffer.ByteBuf
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.ByteToMessageDecoder
import org.slf4j.LoggerFactory
import java.lang.Exception

class KenteDecoder : ByteToMessageDecoder() {
    /**
     * Decode the from one [ByteBuf] to an other. This method will be called till either the input
     * [ByteBuf] has nothing to read when return from this method or till nothing was read from the input
     * [ByteBuf].
     *
     * @param ctx           the [ChannelHandlerContext] which this [ByteToMessageDecoder] belongs to
     * @param buf           the [ByteBuf] from which to read data
     * @param out           the [List] to which decoded messages should be added
     * @throws Exception    is thrown if an error occurs
     */
    override fun decode(ctx: ChannelHandlerContext?, buf: ByteBuf?, out: MutableList<Any>?) {
        lgr.info("${ctx?.channel()?.remoteAddress()} Got Bytes:\n${BcdString.hexString(buf!!, buf.readableBytes(), true)}")

        while (buf.readableBytes() > 0) {
            var le: Exception? = null

            try {
                val p = KentePacket.parse(buf)
                lgr.info("Got KentePacket: ${JSON.toJSONString(p, true)}")

                p?.forEach {
                    out!!.add(it)
                }
            } catch (ex: Exception) {
                le = ex
            }

            if (le is UnknownPacketException) {
                le = null

                try {
                    val dlist = KenteDat1.parse(buf)
                    if (dlist?.isEmpty() != false) throw UnknownPacketException("null for kente-dat1")
                    else {
                        dlist.forEach { out?.add(it) }
                    }
                } catch (ex: Exception) {
                    le = ex
                }
            }

            if (le is UnknownPacketException) {
                le = null

                try {
                    val dlist = KenteWaitComm.parse(buf)
                    if (dlist?.isEmpty() != false) throw UnknownPacketException("null for kente-dat1")
                    else {
                        dlist.forEach { out?.add(it) }
                    }
                } catch (ex: Exception) {
                    le = ex
                }
            }

            if (le is UnknownPacketException) {
                lgr.info("parsing failed, so dicarding all bytes: ${le.message}")
                buf.clear()
            } else {
                break
            }
        }

        lgr.info("Got KentePackets: ${out!!.size}")
    }

    override fun exceptionCaught(ctx: ChannelHandlerContext?, cause: Throwable?) {
        lgr.info("解析异常: ${ctx?.channel()?.remoteAddress()} - ${cause?.message}")

        ctx?.close()
    }

    companion object {
        private val lgr = LoggerFactory.getLogger(KenteDecoder::class.java)
    }
}